package com.kamistoat.meimeistore.modulessystem.controller;

import java.util.*;
import java.util.stream.Collectors;


import com.kamistoat.meimeistore.apisystem.domain.SysRole;
import com.kamistoat.meimeistore.apisystem.domain.SysUser;
import com.kamistoat.meimeistore.apisystem.domain.SysUserRole;
import com.kamistoat.meimeistore.apisystem.entity.SysRoleEntity;
import com.kamistoat.meimeistore.apisystem.entity.SysUserEntity;
import com.kamistoat.meimeistore.commoncore.utils.poi.ExcelUtil;
import com.kamistoat.meimeistore.commoncore.web.controller.BaseController;
import com.kamistoat.meimeistore.commoncore.web.domain.AjaxResult;
import com.kamistoat.meimeistore.commoncore.web.page.TableDataInfo;
import com.kamistoat.meimeistore.commondatascope.annotation.DataScope;
import com.kamistoat.meimeistore.commonlog.annotation.Log;
import com.kamistoat.meimeistore.commonlog.enums.BusinessType;
import com.kamistoat.meimeistore.commonsecurity.annotation.RequiresPermissions;
import com.kamistoat.meimeistore.commonsecurity.utils.SecurityUtils;
import com.kamistoat.meimeistore.modulessystem.service.SysRoleService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;


/**
 * 角色信息表
 *
 * @author kamistoat
 * @email linjianshanke@163.com
 * @date 2022-08-09 10:34:46
 */
@RestController
@RequestMapping("/role")
public class SysRoleController extends BaseController {
    @Autowired
    private SysRoleService sysRoleService;

    /**
     * 匹配列表。不考虑状态。
     */
    @RequiresPermissions("system:role:list")
    @GetMapping("/list")
    public TableDataInfo list(SysRole sysRole) {
        startPage();
        List<SysRoleEntity> sysRoleEntityList = new ArrayList<>();
        if (SecurityUtils.curLoginUserIsAdmin()) {
            sysRoleEntityList = sysRoleService.selectRoleListAll(sysRole);
        } else {
            sysRoleEntityList = sysRoleService.selectRoleList(sysRole, SecurityUtils.getUserId());
        }
        return getDataTable(entity2Vo(sysRoleEntityList));
    }

    /**
     * 导出
     */
    @Log(title = "角色管理", businessType = BusinessType.EXPORT)
    @RequiresPermissions("system:role:export")
    @PostMapping("/export")
    public void export(HttpServletResponse response, SysRole sysRole) {
        List<SysRoleEntity> sysRoleEntityList = new ArrayList<>();
        if (SecurityUtils.curLoginUserIsAdmin()) {
            sysRoleEntityList = sysRoleService.selectRoleListAll(sysRole);
        } else {
            sysRoleEntityList = sysRoleService.selectRoleList(sysRole, SecurityUtils.getUserId());
        }
        ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class);
        util.exportExcel(response, entity2Vo(sysRoleEntityList), "角色数据");
    }

    /**
     * 根据角色编号获取详细信息
     */
    @RequiresPermissions("system:role:query")
    @GetMapping(value = "/{roleId}")
    public AjaxResult getInfo(@PathVariable Long roleId) {
        if (SecurityUtils.curLoginUserIsAdmin() || SecurityUtils.getLoginUser().getRoleIds().contains(roleId)) {
            return AjaxResult.success(entity2Vo(sysRoleService.selectRoleByRoleId(roleId)));
        } else {
            return AjaxResult.error("获取角色信息失败，您没有权限获取该角色信息");
        }
    }

    /**
     * 新增角色
     */
    @RequiresPermissions("system:role:add")
    @Log(title = "角色管理", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysRole sysRole) {
        if (sysRoleService.checkRoleNameUnique(sysRole)) {
            return AjaxResult.error("新增角色'" + sysRole.getRoleName() + "'失败，角色名称已存在");
        } else if (sysRoleService.checkRoleKeyUnique(sysRole)) {
            return AjaxResult.error("新增角色'" + sysRole.getRoleName() + "'失败，角色key已存在");
        }
        sysRole.setCreateBy(SecurityUtils.getUserName());
        return toAjax(sysRoleService.save(vo2Entity(sysRole)));

    }

    /**
     * 修改角色。
     * 这里只允许修改 Entity的 roleName roleKey menuCheckStrictly deptCheckStrictly
     */
    @RequiresPermissions("system:role:edit")
    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysRole sysRole) {
        if (!SecurityUtils.curLoginUserIsAdmin() && !SecurityUtils.getLoginUser().getRoleIds().contains(sysRole.getRoleId())) {
            return AjaxResult.error("修改失败，您无权限修改此角色");
        }
        if (sysRoleService.checkRoleNameUnique(sysRole)) {
            return AjaxResult.error("修改角色'" + sysRole.getRoleName() + "'失败，角色名称已存在");
        } else if (sysRoleService.checkRoleKeyUnique(sysRole)) {
            return AjaxResult.error("修改角色'" + sysRole.getRoleName() + "'失败，角色权限已存在");
        }
        sysRole.setUpdateBy(SecurityUtils.getUserName());
        return toAjax(sysRoleService.editExcludeScope(sysRole));
    }

    /**
     * 修改 data_scope 以及角色绑定的dept
     * 注意这里涉及到修改LoginUser。
     * 注意 角色 是一个很顶层的概念。修改 角色 必然导致底层内容的改变。此时需要联动修改，例如这里的 dept 和 menu 都是 角色 的底层
     */
    @RequiresPermissions("system:role:edit")
    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
    @PutMapping("/dataScope")
    public AjaxResult editRoleDataScope(@RequestBody SysRole sysRole) {
        if (!SecurityUtils.curLoginUserIsAdmin() && !SecurityUtils.getLoginUser().getRoleIds().contains(sysRole.getRoleId())) {
            return AjaxResult.error("修改失败，您无权限修改此角色");
        }
        // 修改 roleEntity 并 更新Role-Dept关系
        boolean authDataScope = sysRoleService.authDataScope(sysRole);
        // 不下线更新缓存中的 核心信息令牌
        boolean dealCoreLoginUser = sysRoleService.dealWithCoreLoginUser(sysRole);

        return toAjax(authDataScope && dealCoreLoginUser);
    }

    /**
     * 修改 角色绑定的menu
     * 注意这里涉及到修改LoginUser。
     * 注意 角色 是一个很顶层的概念。修改 角色 必然导致底层内容的改变。此时需要联动修改，例如这里的 dept 和 menu 都是 角色 的底层
     */
    @RequiresPermissions("system:role:edit")
    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
    @PutMapping("/menu")
    public AjaxResult editRoleMenu(@RequestBody SysRole sysRole) {
        if (!SecurityUtils.curLoginUserIsAdmin() && !SecurityUtils.getLoginUser().getRoleIds().contains(sysRole.getRoleId())) {
            return AjaxResult.error("修改失败，您无权限修改此角色");
        }
        // 更新 菜单权限关系
        boolean authMenu = sysRoleService.authMenu(sysRole);
        // 不下线更新缓存中的 核心信息令牌
        boolean dealCoreLoginUser = sysRoleService.dealWithCoreLoginUser(sysRole);

        return toAjax(authMenu && dealCoreLoginUser);
    }

    /**
     * 修改 角色状态字段
     * 注意这里涉及到修改LoginUser。
     */
    @RequiresPermissions("system:role:edit")
    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
    @PutMapping("/changeStatus")
    public AjaxResult changeStatus(@RequestBody SysRole sysRole) {
        if (!SecurityUtils.curLoginUserIsAdmin() && !SecurityUtils.getLoginUser().getRoleIds().contains(sysRole.getRoleId())) {
            return AjaxResult.error("修改失败，您无权限修改此角色");
        }
        sysRole.setUpdateBy(SecurityUtils.getUserName());

        // 更新entity
        boolean updateStatus = sysRoleService.updateRoleStatus(sysRole);
        // 不下线更新缓存中的 核心信息令牌
        boolean dealCoreLoginUser = sysRoleService.dealWithCoreLoginUser(sysRole);

        return toAjax(updateStatus && dealCoreLoginUser);
    }

    /**
     * 删除角色.要求当前角色没有与其他用户绑定。
     * 删除时将角色的权限绑定和部门绑定一并删除
     */
    @RequiresPermissions("system:role:remove")
    @Log(title = "角色管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{roleIds}")
    public AjaxResult remove(@PathVariable Long[] roleIds) {
        if (!SecurityUtils.curLoginUserIsAdmin()) {
            ArrayList<Long> allowedRoleIds = new ArrayList<>(SecurityUtils.getLoginUser().getRoleIds());
            List<Long> unAllowedRoleIds = Arrays.stream(roleIds)
                    .filter(roleId -> !allowedRoleIds.contains(roleId)).collect(Collectors.toList());
            if (unAllowedRoleIds.size() > 0) {
                return AjaxResult.error("删除失败，您无权限删除id为'" + unAllowedRoleIds.get(0) + "'的角色");
            }
        }
        return toAjax(sysRoleService.deleteRoleByIds(roleIds));
    }

    /**
     * 获取所有角色
     */
    @RequiresPermissions("system:role:query")
    @GetMapping("/optionselect")
    public AjaxResult optionselect() {
        List<SysRoleEntity> sysRoleEntityList = new ArrayList<>();
        if (SecurityUtils.curLoginUserIsAdmin()) {
            sysRoleEntityList = sysRoleService.selectRoleListAll(new SysRole());
        } else {
            sysRoleEntityList = sysRoleService.selectRoleList(new SysRole(), SecurityUtils.getUserId());
        }
        return AjaxResult.success(entity2Vo(sysRoleEntityList));
    }

    /**
     * 查询某个roleId下的授权用户列表。
     * 返回的结果必须是当前登录用户能够访问的数据范围
     * 这里前端用SysUser传递目标roleId，是因为这个接口还承担条件查询的功能。否则直接传一个 roleId就行了
     */
    @RequiresPermissions("system:role:list")
    @GetMapping("/authUser/allocatedList")
    @DataScope
    public TableDataInfo allocatedList(SysUser sysUser) {
        startPage();
        List<SysUserEntity> sysUserEntityList = sysRoleService.selectAllocatedUserList(sysUser);
        List<SysUser> sysUserList = sysUserEntityList.stream().map(sysUserEntity -> {
            SysUser one = new SysUser();
            BeanUtils.copyProperties(sysUserEntity, one);
            return one;
        }).collect(Collectors.toList());
        return getDataTable(sysUserList);
    }

    /**
     * 查询某个roleId下的未授权的用户列表。
     * 返回的结果必须是当前登录用户能够访问的数据范围
     * 这里前端用SysUser传递目标roleId，是因为这个接口还承担条件查询的功能。否则直接传一个 roleId就行了
     */
    @RequiresPermissions("system:role:list")
    @GetMapping("/authUser/unallocatedList")
    public TableDataInfo unallocatedList(SysUser sysUser) {
        startPage();
        List<SysUserEntity> sysUserEntityList = sysRoleService.selectUnAllocatedUserList(sysUser);
        List<SysUser> sysUserList = sysUserEntityList.stream().map(sysUserEntity -> {
            SysUser one = new SysUser();
            BeanUtils.copyProperties(sysUserEntity, one);
            return one;
        }).collect(Collectors.toList());
        return getDataTable(sysUserList);
    }

    /**
     * 批量绑定用户授权
     */
    @RequiresPermissions("system:role:edit")
    @Log(title = "角色管理", businessType = BusinessType.GRANT)
    @PutMapping("/authUser/selectAll")
    public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) {
        if (!SecurityUtils.curLoginUserIsAdmin() && !SecurityUtils.getLoginUser().getRoleIds().contains(roleId)) {
            return AjaxResult.error("您无权限向为该角色分配授权用户");
        }
        return toAjax(sysRoleService.insertAuthUsers(roleId, userIds));
    }

    /**
     * 取消单条授权用户
     */
    @RequiresPermissions("system:role:edit")
    @Log(title = "角色管理", businessType = BusinessType.GRANT)
    @PutMapping("/authUser/cancel")
    public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) {
        if (!SecurityUtils.curLoginUserIsAdmin() && !SecurityUtils.getLoginUser().getRoleIds().contains(userRole.getRoleId())) {
            return AjaxResult.error("您无权限向为该角色取消授权用户");
        }
        return toAjax(sysRoleService.deleteAuthUsers(userRole.getRoleId(), new Long[]{userRole.getRoleId()}));
    }

    /**
     * 批量取消授权用户
     */
    @RequiresPermissions("system:role:edit")
    @Log(title = "角色管理", businessType = BusinessType.GRANT)
    @PutMapping("/authUser/cancelAll")
    public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) {
        if (!SecurityUtils.curLoginUserIsAdmin() && !SecurityUtils.getLoginUser().getRoleIds().contains(roleId)) {
            return AjaxResult.error("您无权限向为该角色分配授权用户");
        }
        return toAjax(sysRoleService.deleteAuthUsers(roleId, userIds));
    }

    /**
     * Entity 和 Vo类的转换
     */
    private List<SysRole> entity2Vo(List<SysRoleEntity> sysRoleEntityList) {
        return
                sysRoleEntityList.stream().map(sysRoleEntity -> {
                    SysRole sysRole = new SysRole();
                    BeanUtils.copyProperties(sysRoleEntity, sysRole);
                    return sysRole;
                }).collect(Collectors.toList());
    }

    private SysRole entity2Vo(SysRoleEntity sysRoleEntity) {
        SysRole sysRole = new SysRole();
        BeanUtils.copyProperties(sysRoleEntity, sysRole);
        return sysRole;
    }

    private List<SysRoleEntity> vo2Entity(List<SysRole> sysRoleList) {
        return
                sysRoleList.stream().map(sysRole -> {
                    SysRoleEntity sysRoleEntity = new SysRoleEntity();
                    BeanUtils.copyProperties(sysRole, sysRoleEntity);
                    return sysRoleEntity;
                }).collect(Collectors.toList());
    }

    private SysRoleEntity vo2Entity(SysRole sysRole) {
        SysRoleEntity sysRoleEntity = new SysRoleEntity();
        BeanUtils.copyProperties(sysRole, sysRoleEntity);
        return sysRoleEntity;
    }

}
